package com.teemor.im.discovery.node;

import com.teemor.im.common.command.EventType;
import com.teemor.im.common.command.RequestType;
import com.teemor.im.common.command.RemoteCommand;
import com.teemor.im.common.exception.ImException;
import com.teemor.im.common.model.ImMessage;
import com.teemor.im.common.util.JsonUtil;
import com.teemor.im.remoting.ReqCallback;
import com.teemor.im.remoting.instance.RemotingService;
import com.teemor.im.remoting.instance.NettyRemotingClient;
import com.teemor.im.remoting.netty.ResponseFuture;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;

/**
 * 每个netty Server 节点
 *
 * @ename zhoulk
 * @cname 周李科
 * @date 2020/6/12 14:20
 */
@Data
@Slf4j
public class ServerNode implements Comparable<ServerNode>, Serializable {

    /**
     * 该客户端的id，每个注册到zk的Node有唯一Id
     */
    private String id;

    /**
     * 连接到该服务端的客户端数量+
     */
    private Integer connect;

    /**
     * 服务IP
     */
    private String host;

    /**
     * 服务监听端口
     */
    private Integer port;

    /**
     * 最后一次活跃时间戳
     */
    private long lastActiveTime;

    //Netty 服务 的连接数
    private Integer balance = 0;

    /**
     * 通信客户端
     */
    private RemotingService remotingService;

    public ServerNode() {
    }

    public ServerNode(String host, Integer port) {
        this.host = host;
        this.port = port;
    }

    @Override
    public int compareTo(ServerNode o) {
        return 0;
    }

    public void incrementBalance() {
        balance++;
    }

    public void decrementBalance() {
        balance--;
    }

    /**
     * 向该节点发送消息
     *
     * @param message
     */
    public void pushMessage(ImMessage message) {
        if (remotingService == null) {
            try {
                this.connect();
            } catch (InterruptedException e) {
                // TODO
            }
        }
        if (remotingService.isActive()) {
            RemoteCommand command = new RemoteCommand();
            command.setContent(JsonUtil.pojoToJson(message));
            command.setEventType(EventType.MESSAGE);
            command.setRequestType(RequestType.REQUEST);

            try {
                ReqCallback callback = new ReqCallback() {
                    int count =0;
                    @Override
                    public void invoke(ResponseFuture responseFuture) {
                        count ++;
                        RemoteCommand response = responseFuture.getResponse();
                        if (response.getEventType().equals(EventType.OK)) {
                            log.info("message push success");
                        } else if (count <= 3){
                            try {
                                remotingService.asyncSend(command, ServerNode.this, this, 1000 * 10);
                            } catch (InterruptedException e) {
                                log.warn("retry push message failed", e);
                            }
                        } else {
                            log.error("message push failed, {}", command.toString());
                        }
                    }
                };
                remotingService.asyncSend(command, this, callback, 1000 * 5);
            } catch (InterruptedException e) {
                log.error("message push failed, {}", command.toString());
            }
        } else {
            throw new ImException("node not active");
        }

    }

    public void push(RemoteCommand command) {
        if (remotingService.isActive()) {
            try {
                remotingService.oneWaySend(command, this, 1000 * 5);
            } catch (InterruptedException e) {
                log.error("message push failed, {}", command.toString());
            }
        } else {
            throw new ImException("node not active");
        }
    }

    private void connect() throws InterruptedException {
        NettyRemotingClient.instance().connect(this);
    }

    @Override
    public boolean equals(Object serverNode) {
        return serverNode instanceof ServerNode && this.id.equals(((ServerNode) serverNode).getId());
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }

}
